package com.xbai.oauth.config;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.security.KeyPair;

/**
 * @author jxbai
 * @desc
 * @date 2021/2/5 0005
 */
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 数据源，用于从数据库获取数据进行认证操作，测试可以从内存中获取
     */
    private final DataSource dataSource;

    /**
     * SpringSecurity 用户自定义授权认证类
     */
    private final UserDetailsService userDetailsService;
    /**
     * 授权认证管理器
     */
    private final AuthenticationManager authenticationManager;

    private final CustomUserAuthenticationConverter customUserAuthenticationConverter;


    /**
     * 客户端信息配置
     * @param clients {@link ClientDetailsServiceConfigurer}
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource).clients(clientDetails());
        /*clients.inMemory()
                //客户端id
                .withClient("changgou")
                //秘钥
                .secret("changgou")
                //重定向地址
                .redirectUris("http://localhost")
                //访问令牌有效期
                .accessTokenValiditySeconds(3600)
                //刷新令牌有效期
                .refreshTokenValiditySeconds(3600)
                .authorizedGrantTypes(
                        //根据授权码生成令牌
                        "authorization_code",
                        //客户端认证
                        "client_credentials",
                        //刷新令牌
                        "refresh_token",
                        //密码方式认证
                        "password"
                )
                //客户端范围，名称自定义，必填
                .scopes("app");*/
    }

    /**
     * 授权服务器端点配置
     * @param endpoints {@link AuthorizationServerEndpointsConfigurer}
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        JwtAccessTokenConverter converter = jwtAccessTokenConverter();
        endpoints.accessTokenConverter(converter)
                //认证管理器
                .authenticationManager(authenticationManager)
                //令牌存储
                .tokenStore(tokenStore(converter))
                //用户信息service
                .userDetailsService(userDetailsService);
    }

    /**
     * 授权服务器的安全配置
     * @param security {@link AuthorizationServerSecurityConfigurer}
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.allowFormAuthenticationForClients()
                .passwordEncoder(new BCryptPasswordEncoder())
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    /**
     * 客户端配置
     *
     * @return {@link ClientDetailsService}
     */
    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 读取密钥的配置
     * @return {@link KeyProperties}
     */
    @Bean("keyProp")
    public KeyProperties keyProperties(){
        return new KeyProperties();
    }

    @Bean
    @Autowired
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    /**
     * JWT令牌转换器
     * @return {@link JwtAccessTokenConverter}
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        KeyProperties keyProperties = keyProperties();
        KeyPair keyPair = new KeyStoreKeyFactory(
                keyProperties.getKeyStore().getLocation(),
                keyProperties.getKeyStore().getSecret().toCharArray()
        ).getKeyPair(
                keyProperties.getKeyStore().getAlias(),
                keyProperties.getKeyStore().getPassword().toCharArray()
        );
        converter.setKeyPair(keyPair);
        //配置自定义的CustomUserAuthenticationConverter
        DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
        return converter;
    }
}
